﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TempestChessComputer
{
    class Piece
    {
        Board board;

        public Piece(PieceType typ, Board b, int id, int player)
        {
            Type = typ;
            board = b;
            PieceId = id;
            Player = player;
            AttackerPieceId = -1;
            FirstMove = true;
        }

        public List<Move> getMoves()
        {
            List<Move> moves = new List<Move>();
            
            if (IsDelayed || IsMoving)
                return moves;

            //first--not moving at all            
            moves.Add(new Move(PieceId, this.X, this.Y));

            switch (Type)
            {
                case PieceType.PAWN:
                    moves.AddRange(getPawnMoves());
                    break;
                case PieceType.KNIGHT:
                    moves.AddRange(getKnightMoves());
                    break;
                case PieceType.BISHOP:
                    moves.AddRange(getBishopMoves());
                    break;
                case PieceType.KING:
                    moves.AddRange(getKingMoves());
                    break;
                case PieceType.QUEEN:
                    moves.AddRange(getQueenMoves());
                    break;
                case PieceType.ROOK:
                    moves.AddRange(getRookMoves());
                    break;
            }

            return moves;
        }

        List<Move> getPawnMoves()
        {
            int xMoveDirection = 0;
            int yMoveDirection = 0;
            int xAttackDirection = 0;
            int yAttackDirection = 0;

            switch (Player)
            {
                case 0:
                    //white
                    yMoveDirection = 1;
                    xAttackDirection = 1;
                    break;
                case 1:
                    //black
                    yMoveDirection = -1;
                    xAttackDirection = 1;
                    break;
                case 2:
                    //green
                    xMoveDirection = 1;
                    yAttackDirection = 1;
                    break;
                case 3:
                    //red
                    xMoveDirection = -1;
                    yAttackDirection = 1;
                    break;
            }            
            List<Move> pawnMoves = new List<Move>();

            //move forward one square
            Move m = new Move(PieceId, X + xMoveDirection, Y + yMoveDirection);
            if(board.isLegalSquare(m.TargetX, m.TargetY))
            {
                Piece p = board.getPieceAtOrMovingTo(m.TargetX, m.TargetY);
                if (p == null)
                    pawnMoves.Add(m);
            }

            //move forward two squares
            if (this.FirstMove)
            {
                m = new Move(PieceId, X + xMoveDirection * 2, Y + yMoveDirection * 2);
                if(board.isLegalSquare(m.TargetX, m.TargetY))
                {
                    Piece p = board.getPieceAtOrMovingTo(m.TargetX, m.TargetY);
                    if (p == null)
                        pawnMoves.Add(m);
                }
            }

            //captures
            Move leftAttack = new Move(PieceId, X + xMoveDirection - xAttackDirection, Y + yMoveDirection - yAttackDirection);
            Move rightAttack = new Move(PieceId, X + xMoveDirection + xAttackDirection, Y + yMoveDirection + yAttackDirection);
            if (board.isLegalSquare(leftAttack.TargetX, leftAttack.TargetY))
            {
                Piece targetPiece = board.getPieceAtOrMovingTo(leftAttack.TargetX, leftAttack.TargetY);
                if (targetPiece != null && targetPiece.Player != Player)
                    pawnMoves.Add(leftAttack);
            }
            if (board.isLegalSquare(rightAttack.TargetX, rightAttack.TargetY))
            {
                Piece targetPiece = board.getPieceAtOrMovingTo(rightAttack.TargetX, rightAttack.TargetY);
                if (targetPiece != null && targetPiece.Player != Player)
                    pawnMoves.Add(rightAttack);
            }

            //en passant     
            if (board.isLegalSquare(X - xAttackDirection, Y - yAttackDirection))
            {
                Piece pawnLeft = board.getPieceAtOrMovingTo(X - xAttackDirection, Y - yAttackDirection);
                if (pawnLeft != null && pawnLeft.Player != Player && pawnLeft.Type == PieceType.PAWN && pawnLeft.FirstMove && board.isLegalSquare(X + xMoveDirection - xAttackDirection, Y + yMoveDirection - yAttackDirection))
                    pawnMoves.Add(new Move(PieceId, X + xMoveDirection - xAttackDirection, Y + yMoveDirection - yAttackDirection, 0, true));            
            }
            if (board.isLegalSquare(X + xAttackDirection, Y + yAttackDirection))
            {
                Piece pawnRight = board.getPieceAtOrMovingTo(X + xAttackDirection, Y + yAttackDirection);
                if (pawnRight != null && pawnRight.Player != Player && pawnRight.Type == PieceType.PAWN && pawnRight.FirstMove && board.isLegalSquare(X + xMoveDirection + xAttackDirection, Y + yMoveDirection + yAttackDirection))
                    pawnMoves.Add(new Move(PieceId, X + xMoveDirection + xAttackDirection, Y + yMoveDirection + yAttackDirection, 0, true));               
            }

            return pawnMoves;
        }

        List<Move> getKnightMoves()
        {
            List<Move> knightMoves = new List<Move>();
            Move m = new Move(PieceId, X - 2, Y - 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                knightMoves.Add(m);

            m = new Move(PieceId, X - 2, Y + 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                knightMoves.Add(m);

            m = new Move(PieceId, X + 2, Y - 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                knightMoves.Add(m);

            m = new Move(PieceId, X + 2, Y + 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                knightMoves.Add(m);

            m = new Move(PieceId, X - 1, Y - 2);
            if (isValidMove(m.TargetX, m.TargetY)) 
                knightMoves.Add(m);

            m = new Move(PieceId, X - 1, Y + 2);
            if (isValidMove(m.TargetX, m.TargetY)) 
                knightMoves.Add(m);

            m = new Move(PieceId, X + 1, Y - 2);
            if (isValidMove(m.TargetX, m.TargetY)) 
                knightMoves.Add(m);

            m = new Move(PieceId, X + 1, Y + 2);
            if (isValidMove(m.TargetX, m.TargetY)) 
                knightMoves.Add(m);            

            return knightMoves;
        }

        List<Move> getBishopMoves()
        {
            List<Move> moves = new List<Move>();
            moves.AddRange(getMovesInDirection(1, 1));
            moves.AddRange(getMovesInDirection(1, -1));
            moves.AddRange(getMovesInDirection(-1, 1));
            moves.AddRange(getMovesInDirection(-1, -1));
            return moves;
        }

        List<Move> getKingMoves()
        {
            List<Move> kingMoves = new List<Move>();

            Move m = new Move(PieceId, X - 1, Y - 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                kingMoves.Add(m);

            m = new Move(PieceId, X - 1, Y);
            if (isValidMove(m.TargetX, m.TargetY)) 
                kingMoves.Add(m);

            m = new Move(PieceId, X - 1, Y + 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                kingMoves.Add(m);

            m = new Move(PieceId, X, Y + 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                kingMoves.Add(m);

            m = new Move(PieceId, X, Y - 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                kingMoves.Add(m);

            m = new Move(PieceId, X + 1, Y + 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                kingMoves.Add(m);

            m = new Move(PieceId, X + 1, Y);
            if (isValidMove(m.TargetX, m.TargetY)) 
                kingMoves.Add(m);

            m = new Move(PieceId, X + 1, Y - 1);
            if (isValidMove(m.TargetX, m.TargetY)) 
                kingMoves.Add(m);

            return kingMoves;
        }

        List<Move> getQueenMoves()
        {
            List<Move> moves = new List<Move>();
            moves.AddRange(getRookMoves());
            moves.AddRange(getBishopMoves());
            return moves;
        }

        List<Move> getRookMoves()
        {
            List<Move> moves = new List<Move>();

            moves.AddRange(getMovesInDirection(1, 0));
            moves.AddRange(getMovesInDirection(-1, 0));
            moves.AddRange(getMovesInDirection(0, 1));
            moves.AddRange(getMovesInDirection(0, -1));
            return moves;
        }

        private List<Move> getMovesInDirection(int xDirection, int yDirection)
        {
            List<Move> moves = new List<Move>();
            int moveX = X + xDirection;
            int moveY = Y + yDirection;

            while (board.isLegalSquare(moveX, moveY))
            {
                Piece target = board.getPieceAtOrMovingTo(moveX, moveY);
                if (target == null || target.Player != Player)
                {
                    moves.Add(new Move(PieceId, moveX, moveY));                    
                }
                if (target != null)
                {
                    break;
                }

                moveX += xDirection;
                moveY += yDirection;
            }

            return moves;
        }
        
        public int X
        {
            get;
            set;
        }

        public int Y
        {
            get;
            set;
        }

        public bool IsMoving
        {
            get;
            set;
        }

        public long MoveStartTime
        {
            get;
            set;
        }

        //x-coord of the destination square (when moving)
        public int TargetX
        {
            get;
            set;
        }

        //y-coord of the destination square (when moving)
        public int TargetY
        {
            get;
            set;
        }

        public PieceType Type
        {
            get;
            set;
        }       

        public bool IsDelayed
        {
            get;
            set;
        }

        public long MoveDelayDuration
        {
            get;
            set;
        }

        public long MoveDelayStart
        {
            get;
            set;
        }

        public long MoveDelayFinishedTime
        {
            get
            {
                return MoveDelayDuration + MoveDelayStart;
            }
        }


        //used by pawns for moving 2 on first move/en passant
        public bool FirstMove
        {
            get;
            set;
        }

        public int PieceId
        {
            get;
            set;
        }

        //0 == controlled by white, 1 == controlled by black, 2 == controlled by green, 3 == controlled by red
        //probably should use an enum for this
        public int Player
        {
            get;
            set;
        }

        public double PieceValue
        {
            get
            {
                switch (Type)
                {
                    case PieceType.BISHOP:
                        return 6;
                    case PieceType.KING:
                        return 10000;
                    case PieceType.KNIGHT:
                        return 6;
                    case PieceType.PAWN:                        
                        return 2;
                    case PieceType.QUEEN:
                        return 18;
                    case PieceType.ROOK:
                        return 10;
                }
                return 0;
            }
        }

        //AttackerPieceId and CaptureTime are populated from a PieceInDanger message
        public int AttackerPieceId
        {
            get;
            set;
        }
        public long CaptureTime
        {
            get;
            set;
        }
        public bool isUnderAttack(long currentTime)
        {
            return AttackerPieceId != -1 && CaptureTime > currentTime;
        }

        //returns true if the target square is on the board and unoccupied by a friendly piece
        private bool isValidMove(int x, int y)
        {
            if (!board.isLegalSquare(x, y))
                return false;

            Piece p = board.getPieceAtOrMovingTo(x, y);
            if (p != null && p.Player == Player)
                return false;

            return true;
        }
    }


    public enum PieceType
    {
        UNKNOWN,
        PAWN,
        BISHOP,
        KNIGHT,
        ROOK,
        QUEEN,
        KING
    }
}
